<?php
// scripts/generate_models_with_base.php
require_once __DIR__ . '/../vendor/autoload.php';

use Illuminate\Database\Capsule\Manager as DB;
use Illuminate\Support\Str;
use Dotenv\Dotenv;

// 手动加载 .env 文件
if (class_exists(Dotenv::class) && file_exists(__DIR__ . '/../.env')) {
    if (method_exists(Dotenv::class, 'createUnsafeMutable')) {
        Dotenv::createUnsafeMutable(__DIR__ . '/..')->load();
    } else {
        Dotenv::createMutable(__DIR__ . '/..')->load();
    }
}

// 初始化数据库连接
$config = require __DIR__ . '/../config/database.php';
$capsule = new DB;
$capsule->addConnection($config['connections']['mysql']); // 根据你的配置调整
$capsule->setAsGlobal();
$capsule->bootEloquent();

class WebmanModelGenerator
{
    private $connection;
    private $modelPath;
    private $basePath;
    private $namespace;
    private $baseNamespace;

    public function __construct()
    {
        $this->connection = DB::connection();
        $this->modelPath = __DIR__ . '/../app/model/';
        $this->basePath = __DIR__ . '/../app/model/Base/';
        $this->namespace = 'app\\model';
        $this->baseNamespace = 'app\\model\\Base';

        // 创建目录
        if (!is_dir($this->modelPath)) {
            mkdir($this->modelPath, 0755, true);
        }
        if (!is_dir($this->basePath)) {
            mkdir($this->basePath, 0755, true);
        }
    }

    public function generateModels($tables = null)
    {
        $tablesToProcess = $tables ? (is_array($tables) ? $tables : [$tables]) : $this->getAllTables();

        foreach ($tablesToProcess as $table) {
            $this->generateModelPair($table);
        }
    }

    private function getAllTables()
    {
        $tables = [];
        $results = DB::select('SHOW TABLES');

        foreach ($results as $result) {
            $tables[] = array_values((array)$result)[0];
        }

        return $tables;
    }

    private function generateModelPair($table)
    {
        $className = $this->getClassName($table);
        $columns = $this->getTableColumns($table);

        // 生成Base模型（总是覆盖）
        $baseModelContent = $this->generateBaseModelContent($className, $table, $columns);
        file_put_contents($this->basePath . $className . '.php', $baseModelContent);
        echo "Generated base model: Base/{$className}.php\n";

        // 生成外部模型（如果不存在才生成）
        $modelFile = $this->modelPath . $className . '.php';
        if (!file_exists($modelFile)) {
            $modelContent = $this->generateModelContent($className);
            file_put_contents($modelFile, $modelContent);
            echo "Generated model: {$className}.php\n";
        } else {
            echo "Model {$className}.php already exists, skipped\n";
        }
    }

    private function getClassName($table)
    {
        $prefix = getenv('DB_PREFIX') ?: '';
        if ($prefix && strpos($table, $prefix) === 0) {
            $table = substr($table, strlen($prefix));
        }
        return Str::studly(Str::singular($table));
    }

    private function getTableColumns($table)
    {
        $columns = [];
        $results = DB::select("DESCRIBE {$table}");

        foreach ($results as $result) {
            $columns[] = [
                'name' => $result->Field,
                'type' => $result->Type,
                'null' => $result->Null === 'YES',
                'key' => $result->Key,
                'default' => $result->Default,
                'extra' => $result->Extra
            ];
        }

        return $columns;
    }

    private function generateBaseModelContent($className, $table, $columns)
    {
        $properties = $this->generateProperties($columns);
        $casts = $this->generateCasts($columns);
        $fillable = $this->generateFillable($columns);

        // 去除前缀，保持与 getClassName 一致
        $prefix = getenv('DB_PREFIX') ?: '';
        $tableForModel = $table;
        if ($prefix && strpos($table, $prefix) === 0) {
            $tableForModel = substr($table, strlen($prefix));
        }

        return "<?php

/**
 * Created by Webman Model Generator.
 */

namespace {$this->baseNamespace};

use support\\Model;
use Carbon\\Carbon;

/**
 * Class {$className}
 * 
{$properties}
 *
 * @package {$this->baseNamespace}
 */
class {$className} extends Model
{
    protected \$table = '{$tableForModel}';
{$casts}{$fillable}
}
";
    }

    private function generateModelContent($className)
    {
        return "<?php

namespace {$this->namespace};

use {$this->baseNamespace}\\{$className} as Base{$className};

/**
 * Class {$className}
 * 
 * @package {$this->namespace}
 */
class {$className} extends Base{$className}
{
    // 在这里添加你的自定义代码
    // 自定义关联、访问器、修改器、作用域等
}
";
    }

    private function generateProperties($columns)
    {
        $properties = [];

        foreach ($columns as $column) {
            $type = $this->mapColumnTypeToPhp($column['type']);
            $nullable = $column['null'] ? '|null' : '';
            $properties[] = " * @property {$type}{$nullable} \${$column['name']}";
        }

        return implode("\n", $properties);
    }

    private function generateCasts($columns)
    {
        $casts = [];

        foreach ($columns as $column) {
            $castType = $this->mapColumnTypeToCast($column['type']);
            if ($castType && !in_array($column['name'], ['created_at', 'updated_at', 'deleted_at'])) {
                $casts[] = "        '{$column['name']}' => '{$castType}'";
            }
        }

        if (empty($casts)) {
            return '';
        }

        return "\n\n    protected \$casts = [\n" . implode(",\n", $casts) . "\n    ];";
    }

    private function generateFillable($columns)
    {
        $fillable = [];

        foreach ($columns as $column) {
            // 排除主键、时间戳字段
            if (!in_array($column['name'], ['id']) &&
                !in_array($column['name'], ['created_at', 'updated_at', 'deleted_at']) &&
                $column['key'] !== 'PRI' &&
                $column['extra'] !== 'auto_increment') {
                $fillable[] = "        '{$column['name']}'";
            }
        }

        if (empty($fillable)) {
            return '';
        }

        return "\n\n    protected \$fillable = [\n" . implode(",\n", $fillable) . "\n    ];";
    }

    private function mapColumnTypeToPhp($columnType)
    {
        // 提取类型名称（去掉长度和其他参数）
        $type = strtolower(preg_replace('/\(.*?\)/', '', $columnType));

        $typeMap = [
            'int' => 'int',
            'integer' => 'int',
            'tinyint' => 'int',
            'smallint' => 'int',
            'mediumint' => 'int',
            'bigint' => 'int',
            'decimal' => 'float',
            'float' => 'float',
            'double' => 'float',
            'real' => 'float',
            'bit' => 'int',
            'boolean' => 'bool',
            'serial' => 'int',
            'date' => 'Carbon',
            'datetime' => 'Carbon',
            'timestamp' => 'Carbon',
            'time' => 'string',
            'year' => 'int',
            'char' => 'string',
            'varchar' => 'string',
            'text' => 'string',
            'tinytext' => 'string',
            'mediumtext' => 'string',
            'longtext' => 'string',
            'binary' => 'string',
            'varbinary' => 'string',
            'blob' => 'string',
            'tinyblob' => 'string',
            'mediumblob' => 'string',
            'longblob' => 'string',
            'enum' => 'string',
            'set' => 'string',
            'json' => 'array',
            'jsonb' => 'array',
        ];

        return $typeMap[$type] ?? 'string';
    }

    private function mapColumnTypeToCast($columnType)
    {
        // 提取类型名称
        $type = strtolower(preg_replace('/\(.*?\)/', '', $columnType));

        $castMap = [
            'int' => 'int',
            'integer' => 'int',
            'tinyint' => 'int',
            'smallint' => 'int',
            'mediumint' => 'int',
            'bigint' => 'int',
            'decimal' => 'float',
            'float' => 'float',
            'double' => 'float',
            'real' => 'float',
            'boolean' => 'bool',
            'date' => 'date',
            'datetime' => 'datetime',
            'timestamp' => 'timestamp',
            'json' => 'array',
            'jsonb' => 'array',
        ];

        // 特殊处理：tinyint(1) 通常用作 boolean
        if (strpos($columnType, 'tinyint(1)') !== false) {
            return 'bool';
        }

        return $castMap[$type] ?? null;
    }
}

// 使用示例
$generator = new WebmanModelGenerator();

// 生成所有表的模型
$generator->generateModels();

// 或者生成指定表的模型
// $generator->generateModels(['admin_apis', 'users']);

// 或者生成单个表的模型
// $generator->generateModels('admin_apis');

echo "Model generation completed!\n";
